home *** CD-ROM | disk | FTP | other *** search
- ////////////////////////////////////////////////////////////////////////////
- //
- // TERMINAL.C - Written by Mike Sax for Dr. Dobb's Journal
- //
- // This file implements a terminal window class "TERMINAL" which you can
- // use to emulate a small tty terminal in a window. If your application
- // has windows of class "TERMINAL", you should call the InitTerminal
- // function at the beginning of your program (not just for the first
- // instance).
- //
- // This file contains one public function:
- //
- // BOOL InitTerminal(HANDLE hInstance); // Return TRUE if success
- //
- // To send characters to the terminal window, you can send a TW_SENDCHAR
- // and TW_SENDSTRING messages. For TW_SENDCHAR, the loword of lParam should
- // contain the character you want to send to the terminal window. For
- // TW_SENDSTRING lParam should be a long pointer to a null-terminated
- // character-string.
- ////////////////////////////////////////////////////////////////////////////
-
- #include <windows.h>
- #include <string.h>
- #include "terminal.h"
-
- // Exported functions:
- LONG FAR PASCAL _export TerminalWndProc(HANDLE hWnd, WORD wMessage,
- WORD wParam, DWORD lParam);
-
- // Static functions:
- void static InitFont(void);
- void static PositionCaret(HWND hWnd);
- void static SendChar(HWND hWnd, int nChar);
- int static Handle(WORD wParam, int nOldValue, int maxValue,
- int nTrackPosition);
-
- // Global variables:
- static char *gszClass = "TERMINAL";
- static HFONT ghFont; // Handle of terminal font
- static int gcxFont; // Width of terminal font
- static int gcyFont; // Height of terminal font
-
- // Set "OEM-font" global variables: ghFont, gxFont, gyFont
- void static InitFont(void)
- {
-
- HDC hDC;
- TEXTMETRIC tm;
-
- ghFont = GetStockObject(OEM_FIXED_FONT);
- hDC = GetDC(NULL);
- SelectObject(hDC, ghFont);
- GetTextMetrics(hDC, &tm);
- ReleaseDC(NULL, hDC);
- gcxFont = tm.tmMaxCharWidth;
- gcyFont = tm.tmHeight;
- }
-
- // This function should be called once for every instance in your program
- // Return TRUE if success
- BOOL InitTerminal(HANDLE hInstance)
- {
- WNDCLASS wc;
-
- InitFont();
- // If the terminal class was registered by a previous instance, we can
- // return success
- if (GetClassInfo(hInstance, gszClass, &wc))
- return TRUE;
- wc.style = 0;
- wc.lpfnWndProc = TerminalWndProc;
- wc.cbClsExtra = 0;
- wc.cbWndExtra = sizeof(HTERMINAL);
- wc.hInstance = hInstance;
- wc.hIcon = NULL;
- wc.hCursor = LoadCursor(NULL, IDC_ARROW);
- wc.hbrBackground = GetStockObject(BLACK_BRUSH);
- wc.lpszMenuName = NULL;
- wc.lpszClassName = gszClass;
- return RegisterClass(&wc);
- }
-
- // Position the caret in the window IMPORTANT: This function should only be
- // called when the window has the input focus!
- void static PositionCaret(HWND hWnd)
- {
- HTERMINAL hTerminal = GetWindowWord(hWnd, 0);
- NPTERMINAL npTerminal = (NPTERMINAL)LocalLock(hTerminal);
-
- if (NULL == npTerminal)
- return; // Abort function
- SetCaretPos(gcxFont * (npTerminal->xCursor - npTerminal->xOffset),
- gcyFont * (npTerminal->yCursor - npTerminal->yOffset + 1) - 1);
- LocalUnlock(hTerminal);
- }
-
- // Send a character to a terminal window (internal function)
- void static SendChar(HWND hWnd, int nChar)
- {
- HTERMINAL hTerminal = GetWindowWord(hWnd, 0);
- NPTERMINAL npTerminal = (NPTERMINAL)LocalLock(hTerminal);
-
- if (NULL == npTerminal)
- return; // Abort function
- switch(nChar)
- {
- case '\t': // Tab
- npTerminal->xCursor += TABSIZE - (npTerminal->xCursor % TABSIZE);
- break;
- case '\r': // Return
- npTerminal->xCursor = 0;
- break;
- case '\n': // New line
- npTerminal->yCursor++;
- break;
- case '\a': // Beep
- MessageBeep(0);
- break;
- case '\xC': // Clear screen
- memset(npTerminal->achBuffer[0], ' ', ROWS * COLUMNS);
- npTerminal->xCursor = npTerminal->yCursor = 0;
- InvalidateRect(hWnd, NULL, TRUE);
- break;
- case '\b': // Backspace
- if (npTerminal->xCursor)
- npTerminal->xCursor--;
- break;
- default:
- {
- HDC hDC;
- npTerminal->achBuffer[npTerminal->yCursor]
- [npTerminal->xCursor] = (char)nChar;
-
- if (hDC = GetDC(hWnd))
- {
- SelectObject(hDC, ghFont);
- SetBkColor(hDC, 0l);
- SetTextColor(hDC, RGB(255, 128, 128));
- HideCaret(hWnd); // Don't paint over the caret
- TextOut(hDC,
- gcxFont * (npTerminal->xCursor - npTerminal->xOffset),
- gcyFont * (npTerminal->yCursor - npTerminal->yOffset),
- (LPSTR)&nChar, 1);
- ShowCaret(hWnd);
- ReleaseDC(hWnd, hDC);
- }
-
- npTerminal->xCursor++;
- }
- }
- if (npTerminal->xCursor >= COLUMNS)
- {
- npTerminal->xCursor = 0;
- npTerminal->yCursor++;
- }
- if (npTerminal->yCursor >= ROWS)
- {
- npTerminal->yCursor = ROWS - 1;
- memmove(npTerminal->achBuffer[0], npTerminal->achBuffer[1],
- (ROWS - 1) * COLUMNS);
- memset(npTerminal->achBuffer[ROWS - 1], ' ', COLUMNS);
- ScrollWindow(hWnd, 0, -gcyFont, NULL, NULL);
- UpdateWindow(hWnd); // Send WM_PAINT message now
- }
- if (hWnd == GetFocus())
- PositionCaret(hWnd);
- LocalUnlock(hTerminal);
- }
-
- // Handle WM_VSCROLL or WM_HSCROLL message.
- // Returns the new scrollbar position
- int static HandleScroll(WORD wParam, int nOldValue, int maxValue,
- int nTrackPosition)
- {
- int nNewValue = nOldValue;
-
- switch(wParam)
- {
- case SB_BOTTOM:
- nNewValue = maxValue - 1;
- break;
- case SB_LINEUP:
- --nNewValue;
- break;
- case SB_LINEDOWN:
- ++nNewValue;
- break;
- case SB_PAGEUP:
- nNewValue -= maxValue / 5;
- break;
- case SB_PAGEDOWN:
- nNewValue += maxValue / 5;
- break;
- case SB_TOP:
- nNewValue = 0;
- case SB_THUMBPOSITION:
- nNewValue = nTrackPosition;
- break;
- }
- if (nNewValue < 0)
- nNewValue = 0;
- if (nNewValue > maxValue)
- nNewValue = maxValue;
- return nNewValue;
- }
-
- // This is the window function of the terminal class
- LONG FAR PASCAL _export TerminalWndProc(HANDLE hWnd, WORD wMessage,
- WORD wParam, DWORD lParam)
- {
-
- switch(wMessage)
- {
- case WM_CREATE:
- {
- HTERMINAL hTerminal = LocalAlloc(LHND, sizeof(TERMINAL));
- NPTERMINAL npTerminal;
-
- if (!hTerminal)
- return -1; // Fail CreateWindow
- npTerminal = (NPTERMINAL)LocalLock(hTerminal);
- if (npTerminal == NULL)
- {
- LocalFree(hTerminal);
- return -1;
- }
- memset((PSTR)npTerminal->achBuffer, ' ', ROWS * COLUMNS);
- SetWindowWord(hWnd, 0, (WORD)hTerminal);
- LocalUnlock(hTerminal);
- }
- break;
- case WM_HSCROLL:
- {
- HTERMINAL hTerminal = GetWindowWord(hWnd, 0);
- NPTERMINAL npTerminal = (NPTERMINAL)LocalLock(hTerminal);
- int xNewOffset;
-
- if (NULL == npTerminal)
- return 0l; // abort
- xNewOffset = HandleScroll(wParam, npTerminal->xOffset,
- max(0, COLUMNS - npTerminal->cxWindow), LOWORD(lParam));
- if (xNewOffset != npTerminal->xOffset)
- {
- ScrollWindow(hWnd, gcxFont * (npTerminal->xOffset -
- xNewOffset), 0, NULL, NULL);
- npTerminal->xOffset = xNewOffset;
- SetScrollPos(hWnd, SB_HORZ, xNewOffset, TRUE);
- }
- LocalUnlock(hTerminal);
- }
- break;
- case WM_VSCROLL:
- {
- HTERMINAL hTerminal = GetWindowWord(hWnd, 0);
- NPTERMINAL npTerminal = (NPTERMINAL)LocalLock(hTerminal);
- int yNewOffset;
-
- if (NULL == npTerminal)
- return 0l; // abort
- yNewOffset = HandleScroll(wParam, npTerminal->yOffset,
- max(0, ROWS - npTerminal->cyWindow), LOWORD(lParam));
- if (yNewOffset != npTerminal->yOffset)
- {
- ScrollWindow(hWnd, 0, gcyFont * (npTerminal->yOffset -
- yNewOffset), NULL, NULL);
- npTerminal->yOffset = yNewOffset;
- SetScrollPos(hWnd, SB_VERT, yNewOffset, TRUE);
- }
- LocalUnlock(hTerminal);
- }
- break;
- case WM_DESTROY:
- LocalFree(GetWindowWord(hWnd, 0));
- break;
- case WM_PAINT:
- {
- int i;
- PAINTSTRUCT ps;
- HDC hDC;
- HTERMINAL hTerminal = GetWindowWord(hWnd, 0);
- NPTERMINAL npTerminal = (NPTERMINAL)LocalLock(hTerminal);
-
- if (NULL == npTerminal)
- return 0l; // Can't access our parameters->abort paint
- hDC = BeginPaint(hWnd, &ps);
- SelectObject(hDC, ghFont);
- SetBkColor(hDC, 0l);
- SetTextColor(hDC, RGB(255, 128, 128));
- for(i = 0 ; i < ROWS ; ++i)
- TextOut(hDC, - (npTerminal->xOffset * gcxFont),
- gcyFont * (i - npTerminal->yOffset),
- npTerminal->achBuffer[i], COLUMNS);
- EndPaint(hWnd, &ps);
- LocalUnlock(hTerminal);
- }
- break;
- case WM_SIZE:
- {
- HTERMINAL hTerminal = GetWindowWord(hWnd, 0);
- NPTERMINAL npTerminal = (NPTERMINAL)LocalLock(hTerminal);
- RECT rect;
-
- if (NULL == npTerminal)
- return 0l;
- // Get client dimensions without scroll bars (we have no border)
- GetWindowRect(hWnd, &rect);
- // Set max rows and columns that can be displayed
- npTerminal->cxWindow = (rect.right - rect.left) / gcxFont;
- npTerminal->cyWindow = (rect.bottom - rect.top) / gcyFont;
- SetScrollRange(hWnd, SB_HORZ, 0,
- max(0, COLUMNS - npTerminal->cxWindow), TRUE);
- SetScrollRange(hWnd, SB_VERT, 0,
- max(0, ROWS - npTerminal->cyWindow), TRUE);
- LocalUnlock(hTerminal);
- }
- break;
- case TW_SENDCHAR:
- SendChar(hWnd, wParam);
- break;
- case TW_SENDSTRING:
- while ((LPSTR)(lParam))
- SendChar(hWnd, *((LPSTR)(lParam++)));
- break;
- case WM_CHAR:
- // Send a notification message to our parent
- SendMessage(GetParent(hWnd), WM_COMMAND,
- GetWindowWord(hWnd, GWW_ID), MAKELONG(hWnd, wParam));
- break;
- case WM_LBUTTONDOWN:
- SetFocus(hWnd);
- case WM_SETFOCUS:
- CreateCaret(hWnd, NULL, gcxFont, 0);
- PositionCaret(hWnd);
- ShowCaret(hWnd);
- break;
- case WM_KILLFOCUS:
- DestroyCaret();
- break;
- case WM_GETDLGCODE:
- return DLGC_WANTALLKEYS; // We also process enter, tab, ...
- default:
- return DefWindowProc(hWnd, wMessage, wParam, lParam);
- }
- return 0l;
- }
-